#include <stdlib.h>
#include <string.h>
#include "tools.h"
#include "nandfs.h"
#include "shared1.h"
#include "iplsave.h"
#include "titles.h"
#include "wii-fsck.h"

// This chunk will check the validity of iplsave.bin,
// as well as read it.

#define IPLSAVE_DEBUG
#ifdef IPLSAVE_DEBUG
#	define	dbgprintf(f, arg...) do { printf("iplsave: " f, ##arg); } while(0)
#else
#	define	dbgprintf(f, arg...) 
#endif

static struct nandfs *__iplsave_fs = NULL;
static u8 *iplsave_buffer = NULL;
//static iplsave *__iplsave_list = NULL;
static u32 __iplsave_size = 0;
static int __iplsave_inited = 0;

static int __check_iplsave_sanity(void)
{
	if (memcmp(iplsave_buffer, "RIPL", 4)) {
		dbgprintf("iplsave.bin header is not \'RIPL\' (%02x%02x%02x%02x). It is \'%c%c%c%c\' (%02x%02x%02x%02x)\n",
			'R', 'I', 'P', 'L', iplsave_buffer[0], iplsave_buffer[1], iplsave_buffer[2], iplsave_buffer[3],
			iplsave_buffer[0], iplsave_buffer[1], iplsave_buffer[2], iplsave_buffer[3]);
		return 0;
	}
	if(be32(iplsave_buffer + 4) != __iplsave_size) {
		dbgprintf("iplsave.bin (%u) is not the same size as the header said it should be (%u)\n", (u32)__iplsave_size, be32(iplsave_buffer+4));
		return 0;
	}
	u8 md5hash[0x10];
	md5(iplsave_buffer, __iplsave_size - 0x10, md5hash);
	if(memcmp(md5hash, iplsave_buffer + __iplsave_size - 0x10, 0x10) != 0) {
		dbgprintf("iplsave.bin's hash (%16llx%16llx) is invalid! Should be (%16llx%16llx)!\n",
			be64(iplsave_buffer + __iplsave_size - 0x10), be64(iplsave_buffer + __iplsave_size - 8), be64(md5hash), be64(md5hash + 8));
		return 0;
	}
	int i;
	int retval = 1;
	for(i = 1; i <= 0x30; i++) {
		iplsave_entry entry;
		entry.channel_type = iplsave_buffer[0x10 * i];
		entry.secondary_type = iplsave_buffer[0x10 * i];
		entry.flags = be16(iplsave_buffer + 0x10 * i + 6);
		entry.title_id = be64(iplsave_buffer + 0x10 * i + 8);
		if ((entry.channel_type != 0) && (entry.channel_type != 1)) {
			printf("type1=%02hhx type2=%02hhx flags=%02hhx tid=%16llx\n", entry.channel_type, entry.secondary_type, entry.flags, entry.title_id);
			int result = check_title_integrity(TITLE_HIGH(entry.title_id), TITLE_LOW(entry.title_id));
			int error = 0;
			if(result & TITLE_TMDENOENT)		error = 1;
			if(result & TITLE_TIKENOENT)		error = 1;
			if(result & TITLE_UIDENOENT)		error = 1;
			if(result & TITLE_SIGINVAL)		error = 1;
			if(result & TITLE_CONTENTENOENT)	error = 1;
			if(result & TITLE_CONTENTHASH)		error = 1;
			if(error) {
				dbgprintf("Channel %08x-%08x in the iplsave.bin is invalid (ret = %d, entry = %d)\n", TITLE_HIGH(entry.title_id), TITLE_LOW(entry.title_id), result, i);
				retval = 0;
			}
		}
	}
	return retval;
}

void iplsave_init(struct nandfs *fs)
{
	__iplsave_fs = fs;
	struct nandfile *iplsave_file;
	u32 bytes_read = 0;
	
	iplsave_file = nandfs_open(__iplsave_fs, "/title/00000001/00000002/data/iplsave.bin");
	if (iplsave_file == NULL) {
		dbgprintf("iplsave.bin could not be opened\n");
		return;
	}
	// Let them init all they want.
	if(iplsave_buffer != NULL)
		free(iplsave_buffer);
	__iplsave_size = iplsave_file->entry.size;
	iplsave_buffer = calloc(__iplsave_size, 1);
	bytes_read = nandfs_read(iplsave_buffer, __iplsave_size, iplsave_file);
	if (bytes_read != __iplsave_size)
		fatal("Couldn't read file!");
	nandfs_close(iplsave_file);
	__iplsave_inited = __check_iplsave_sanity();
}	

/*iplsave* iplsave_get_list(iplsave* target)
{
	if(__iplsave_inited == 0)
		fatal("iplsave.bin was not inited yet!");
	if(target == NULL)
		fatal("Tried to pass a NULL pointer to iplsave_get_list!");
	memcpy(target, __iplsave_list, __iplsave_size);
	return target;
}*/

int iplsave_get_size()
{
	return __iplsave_size;
}

int check_channel_validity()
{
	if(__iplsave_inited == 1)
		return WIIFSCK_OK;
	else
		return WIIFSCK_INVAL;
}
